from threading import *
import re
import time
from struct import pack, unpack
from collections import deque

class MessageHandler(threading.Thread):
    
    def __init__(self, logger, sender, server):
        threading.Thread.__init__(self)
        self.logger = logger
        self.mSender = sender
        self.queue = deque()
        self.server = server
        
#-------------------------------------------------------------------------------------------------------        
    def add_queue(data, address):
        queue.append(data, address)
        
#-------------------------------------------------------------------------------------------------------            	

    # create valid message struct and return the string.
    def create_message(msg_type, msgno, *params):
        msg = ' '.join(params)
        if len(msg) + 6 > 200:
            raise ArgumentError("message too long!")  
        
        # dit kan ook werken, want ik weet nog niet welke van ze goed is
        # format = '>hhH'+(len(msg))+'s'
        # return struct.pack(format, len(msg)+6, type, msgno, msg)
        return struct.pack(">hhH%ds" % len(msg), len(msg) + 6, msg_type, msgno, msg)
#-------------------------------------------------------------------------------------------------------                
    def run(self):
        while True:
            try:
                data, address = queue.popleft()
                analyse_data(data, address)
            except IndexError:
                pass

#-------------------------------------------------------------------------------------------------------                    

    # incoming data and incoming address of source/sender
    def analyse_data(data, address):
		
        length, type, msgno, message = unpack(">hhH%ds" % (len(data)-6), data)
        
        # dict van berichttypen

        messages = {
            "100": msg100(message),
            "110": msg110(message),
            "120": msg120(message),
            "130": msg130(message),
            "140": msg140(message),
            "150": msg150(),
            "160": msg160(message),
            "170": msg170(message),
            "200": msg200_210("200", message),
            "210": msg200_210("210", message),
            "300": msg300_310("300", message),
            "310": msg300_310("310", message),
            "600": msg600(message),
            "602": msg602(message),
            "604": msg604(message),
            "700": logger.add_queue("700 is er nog niet", "MessageHandler"),
            "701": msg701(message)
        }
        # dict lookup om berichten af te handelen
        messages[type]

        # registrate client, upon failure: call msg510, upon success: call 500
        def msg100(message):
            name, pw = message.split()  
            if not re.match("^[A-Za-z0-9_]*$", name):
                new_message = "Registration from "+address+" failed. "+\
                                    name+" contains illegal name characters\n"
                logger.add_queue(new_message, "MessageHandler")
                new_message = create_message(510, 1, new_message)                    
                mSender.add_queue(new_message, address)
            else:                            
                for client in server.clients:
                    if (name != client.name):                       
                        # check if it was an admin who logged in
                        if pw:
                            if pw != ADMINS[name]:
                                # incorrect passw, send registration failed
                                new_message = create_message(510, 1, "Registration failed: wrong \
                                                                password provided. ")                    
                                mSender.add_queue(new_message, address)
                               
                            else:
                                # correct passw, send registration succeeded
                                flag = 1
                        else:
                            # not an admin, just send registration succeeded
                            flag = 1
                    # name already exists
                    else:
                        # send registration failed
                        new_message = create_message(510, 1, "Registration failed: name already in use. ")                    
                        mSender.add_queue(address, new_message)

            # log in succeeded, now add to the dictionary and broadcast (generate 110)
            if flag:
                logger.add_queue("Client added, name: "+name+'\n', "MessageHandler")
                # add client to database

                new_message = create_message(500, 1, "")                    
                mSender.add_queue(new_message, address)
                
                c = client(address, name, 1 , server)
                server.clients.append(c)
                           
                # tell all the other servers.

                # volgnummer is het aantal berichten verstuurd naar een
                # bepaalde server dus de eerste is 1 enz.
                mSender.msg110(1, len(server.clients), name)
							
#------------------------------------------------------------------------------------------------------        
        # 
        def msg110(message):
            # assume msgno, length and name are seperated by  white spaces
            msgno, list_len, name = message.split()
            
            # server's address where this message came from
            c = client(address, name, 1, None)
            server.clients.append(c)
        
#------------------------------------------------------------------------------------------------------                
        # this function is called when this clients wants to end the connection
        # write to log 
        # call msg130 to notificate the world

        def msg120(message):
            for client in server.clients:
                if(address == client.address):
                    server.clients.remove(client)           
                    # notificate only this client
                    msg = "Client "+client.name+" disconnected."
                    logger.add_queue(msg, "MessageHandler")
                    break
            if message:
                # data can be a greeting sent by the leaving client
                # notificate clients, servers and control server
                mSender.msg130(client.name, message)
            else:
                mSender.msg130(client.name, msg)
#-------------------------------------------------------------------------------------------------------
        # this function is called when this clients wants to end the connection
        # write to log 
        # call msg130 to notificate the world

        def msg130(message):
            name, message = messag.split()
            for client in server.clients:
                if(name == client.name):
                    server.clients.remove(client)           
                    # notificate only this client
                    msg = "Client "+client.name+" disconnected."
                    logger.add_queue(msg, "MessageHandler")
                    break
            if message:
                # data can be a greeting sent by the leaving client
                # notificate clients, servers and control server
                mSender.msg130(client.name, message)
            else:
                mSender.msg130(client.name, msg)
#-------------------------------------------------------------------------------------------------------
        # receives ping
        def msg140(identification):
            mSender.msg150(identification, address)
            
#-------------------------------------------------------------------------------------------------------
        # Onze message handler moet gewoon 150 kunnen ontvangen
		# receive pong
        def msg150():
            for child in server.children:
                if (address == child.address):
                    child.last_reacted = time.time()
                        
            for client in self.server.clients:
                 if (address == child.address):
                    child.last_reacted = time.time()
                        
            if(address == server.parent):
                server.parent.last_reacted = time.time()
#-------------------------------------------------------------------------------------------------------  
   
	    # client wants to change name
        def msg160(newname):
            for client  in self.server.clients:
                if address == client.address:
                    oldname = client.name
                    if re.match("^[A-Za-z0-9_]*$", newname):
                        if(newname == oldname):
                        # failed to change name
                            msg = "Requested name is the same as your current name."
                            logger.add_queue(msg, "MessageHandler")
                            new_message = create_message(530 , 1 , msg)
                            mSender.add_queue(new_message, address)
                        else:
                            for c in self.server.clients:
                                if newname != c.name:                            
                                    client.name = newname
                                    new_message = create_message(520 , 1 ,"" )
                                    mSender.add_queue(new_message, address)
                                    mSender.msg170(oldname, newname)
                                    break
                                else:
                                    msg = "The requested name allready exists." 
                                    logger.add_queue(msg, "MessageHandler")
                                    new_message = create_message(530 , 1 ,msg)
                                    mSender.add_queue(new_message, address)
                    else:
                        msg = "Name may only consist of letters, numbers or underscores." 
                        logger.add_queue(msg, "MessageHandler")
                        new_message = create_message(530 , 1 ,msg )
                        mSender.add_queue(new_message, address)

#-------------------------------------------------------------------------------------------------------       		

        def msg170(message):
            oldname, newname = message.split()
            for child in self.server.children:
                msg = oldname + " " + newname
                msg = create_message(170, 1, msg)
                mSender.add_queue(msg, child.address)
            for client in self.server.clients:
                if client.name == oldname:
                    client.name = newname
                    break 
                    
        # client sent this text/action message
        def msg200_210(msg_type, message):
            destination, text = message.split()
            
            msg = "Send " + msg_type + " message to " + destination
            logger.add_queue(msg, "MessageHandler")
            
            if msg_type == "200":
                type = 300
            elif msg_type == "210":
                type = 310
            for client  in server.clients():
                if address == client.address:
                    mSender.msg300_310(client.name, destination, text, type)


        def msg300_310(msg_type, message):
          for client in self.server.clients:
            msg = create_message(msg_type, 1, message)
            mSender.add_queue(msg, client.address)
          
          logger.add_queue(message, "MessageHandler")
#-------------------------------------------------------------------------------------------------------
        def msg600(tag):
            for server in server.children():
                if tag != server.tag:
                    server.childeren.append((address, tag))
                    msender.msg110(1, len(server.childeren()), tag)                   
#-------------------------------------------------------------------------------------------------------
      
        def msg602(text):
            ip, port, tag = text.split(':', 2)
            server.parent = (ip, port)
            logger.add_queue("sending a 600 to %s:%s:%s after receiving a %s" % (ip, port, tag), "MessageHandler")
            send_msg = create_message(600, 1, server.tag)
            mSender.add_queue(send_msg, server.parent)
#-------------------------------------------------------------------------------------------------------                    
        def msg604(text):
            first , second = text.split()
            ip, port, tag = first.split(':', 2)
            ip_parent ,port_parent, tag_parent  = second.split(':', 2)
            if(server.parent == (ip, port)):
                 send_msg = create_message(600, 1, server.tag)
                 mSender.add_queue(send_msg, (ip_parent, port_parent))
            else:
                for client in server.clients:
                    if(client.address == (ip, port)):
                        server.clients.remove(client)
                        
#-----------------------------------------------------------------------------
		

